/*
 * x86_64/descs.hh - Descriptors-related data types and functions.
 * Copyright (C) 2007  Mikhail Vorozhtsov
 * See the LICENSE section of the README file for details.
 */

/* $Id$ */

#ifndef HEADER_X86_64_DESCS_H
#define HEADER_X86_64_DESCS_H

#include HEADER (utils,setters.hh)

START_NAMESPACE (x86_64)

struct dt_desc_t
{
  u16_t limit;
  u64_t base;
  
  INLINE
  dt_desc_t (u64_t base, u16_t limit)
  {
    this->base = base;
    this->limit = limit;
  }
} PACKED;

INLINE
static void
set_gdt (u64_t base, u16_t limit)
{
  dt_desc_t desc (base, limit);
  asm volatile (
    "lgdt %0\n"
    :
    : "m"(desc)
  );
}

INLINE
static void
set_idt (u64_t base, u16_t limit)
{
  dt_desc_t desc (base, limit);
  asm volatile (
    "lidt %0\n"
    :
    : "m"(desc)
  );
}

union desc_t
{
  enum type_t
  {
    TYPE_CODE = 0x0B,
    TYPE_DATA = 0x03
  };

  struct
    {
      u64_t : 40;
      u64_t type : 4;
      u64_t S : 1;
      u64_t DPL : 2;
      u64_t P : 1;
      u64_t : 5;
      u64_t L : 1;
      u64_t D : 1;
      u64_t : 9;
    };
  u64_t raw;

  UTILS_SETTER (desc_t, type_t, type)
  UTILS_SETTER (desc_t, u64_t, DPL)
  UTILS_BOOL_SETTER (desc_t, S)
  UTILS_BOOL_SETTER (desc_t, P)
  UTILS_BOOL_SETTER (desc_t, L)
  UTILS_BOOL_SETTER (desc_t, D)
};

union sys_desc_t
{
  enum type_t
  {
    TYPE_LDT = 0x02,
    TYPE_TSS = 0x09,
    TYPE_BUSY_TSS = 0x0B,
    TYPE_CALL = 0x0C,
    TYPE_INT = 0x0E,
    TYPE_TRAP = 0x0F
  };

  struct
    {
      union
        {
          u64_t limit_low : 16;
          u64_t offset_low : 16;
        } PACKED;
      union
        {
          u64_t base_low : 24;
          struct
            {
              u64_t selector : 16;
              u64_t IST : 3;
              u64_t : 5;
            } PACKED;
        } PACKED;
      u64_t type : 4;
      u64_t S : 1;
      u64_t DPL : 2;
      u64_t P : 1;
      union
        {
          struct
            {
              u64_t limit_high : 4;
              u64_t AVL : 1;
              u64_t : 2;
              u64_t G : 1;
              u64_t base_high : 40;
            } PACKED;
          u64_t offset_high : 48;
        } PACKED;
      u64_t : 32;
    } PACKED;
  u64_t raw[2];

  INLINE
  u32_t
  get_limit ()
  {
    return limit_low + (limit_high << 16);
  }

  INLINE
  sys_desc_t &
  set_limit (u32_t limit)
  {
    limit_low = (limit & 0xFFFF);
    limit_high = (limit >> 16) & 0xFF;
    return *this;
  }

  INLINE
  u64_t
  get_base ()
  {
    return base_low + (base_high << 24);
  }

  INLINE
  sys_desc_t &
  set_base (u64_t base)
  {
    base_low = base & 0xFFFFFF;
    base_high = base >> 24;
    return *this;
  }

  INLINE
  u64_t
  get_offset ()
  {
    return offset_low + (offset_high << 16);
  }

  INLINE
  sys_desc_t &
  set_offset (u64_t offset)
  {
    offset_low = offset & 0xFFFF;
    offset_high = offset >> 16;
    return *this;
  }

  UTILS_SETTER (sys_desc_t, type_t, type)
  UTILS_SETTER (sys_desc_t, u64_t, DPL)
  UTILS_SETTER (sys_desc_t, u64_t, IST)
  UTILS_SETTER (sys_desc_t, u64_t, selector)
  UTILS_BOOL_SETTER (sys_desc_t, S)
  UTILS_BOOL_SETTER (sys_desc_t, P)
};

END_NAMESPACE

#endif /* HEADER_X86_64_DESCS_HH */

